
import flash.geom.Point;
import charts.series.dots.Point;

class ScreenCoords extends ScreenCoordsBase {
	
	var x_range:Range;
	var y_range:Range;
	var y_right_range:Range;
	
	// position of the zero line
	//public var zero:Float=0;
	//public var steps:Float=0;
	
	// tick_offset is set by 3D axis
	public var tick_offset:Float;
	var x_offset:Bool;
	var y_offset:Bool;
	var bar_groups:Float;

	
	public function new (
		top:Float, left:Float, right:Float, bottom:Float,
		y_axis_range:Range,
		y_axis_right_range:Range,
		x_axis_range:Range,
		x_left_label_width:Float,
		x_right_label_width:Float,
		three_d:Bool
	) {
		super( top, left, right, bottom );
		
		var tmp_left:Int = left;
		
		this.x_range = x_axis_range;
		this.y_range = y_axis_range;
		this.y_right_range = y_axis_right_range;
		
		// trace( '-----' );
		// trace( this.x_range.count() );
		// trace( this.y_range.count() );
		
		
		if( x_range ) {
			right = this.jiggle( left, right, x_right_label_width, x_axis_range.count() );
			tmp_left = this.shrink_left( left, right, x_left_label_width, x_axis_range.count() );
		}
		
		this.top = top;
		this.left = Math.max(left, tmp_left);
		
		// round this down to the nearest Int:
		this.right = Math.floor( right );
		this.bottom = bottom;
		this.width = this.right-this.left;
		this.height = bottom-top;
		
		if( three_d )
		{
			// tell the box object that the
			// X axis labels need to be offset
			this.tick_offset = 12;
		}
		else
			this.tick_offset = 0;
		
		//
		//  x_offset:
		//
		//   False            True
		//
		//  |               |
		//  |               |
		//  |               |
		//  +--+--+--+      |-+--+--+--+-+
		//  0  1  2  3        0  1  2  3
		//

		// PIE charts don't have these:
		if( x_axis_range ) {
			this.x_offset = x_axis_range.offset;
		}
		if( y_axis_range ) {
			trace( 'YYYY', y_axis_range.offset );
			this.y_offset = y_axis_range.offset;
		}
			
		this.bar_groups = 1;
	}
	
	//
	// if the last X label is wider than the chart area, the last few letters will
	// be outside the drawing area. So we make the chart width smaller so the label
	// will fit Into the screen.
	//
	//DZ: this implementation chops off the last label on scatter charts because it
	//    assumes the label is centered on the last "item" (like a bar) instead of 
	//    at the max edge of the plot.
	public function jiggle_original( left:Float, right:Float, x_label_width:Float, count:Float ): Float {
		var r:Int = 0;

		if( x_label_width != 0 )
		{
			var item_width:Int = (right-left) / count;
			r = right - (item_width / 2);
			var new_right:Int = right;
			
			// while the right most X label is off the edge of the
			// Stage, move the box.right - 1
			while( r+(x_label_width/2) > right )
			{
				new_right -= 1;
				// changing the right also changes the item_width:
				item_width = (new_right-left) / count;
				r = new_right-(item_width/2);
			}
			right = new_right;
		}
		return right;
	}
	
	//DZ: this implementation probably add white space on the right side of a
	//    non-scatter type plot because it assumes that the label is centered at
	//    the max edge of the plot instead of centered on the last "item" 
	//    (like a bar)
	public function jiggle( left:Float, right:Float, x_label_width:Float, count:Float ): Float {
		return right - (x_label_width / 2);
	}
	
	//
	// if the left label is truncated, shrink the box until
	// it fits onto the screen
	//
	public function shrink_left( left:Float, right:Float, x_label_width:Float, count:Float ): Float {
		var pos:Int = 0;

		if( x_label_width != 0 )
		{
			var item_width:Int = (right-left) / count;
			pos = left+(item_width/2);
			var new_left:Int = left;
			
			// while the left most label is hanging off the Stage
			// move the box.left in one pixel:
			while( pos-(x_label_width/2) < 0 )
			{
				new_left += 1;
				// changing the left also changes the item_width:
				item_width = (right-new_left) / count;
				pos = new_left+(item_width/2);
			}
			left = new_left;
		}
		
		return left;
		
	}
	
	//
	// the bottom Point of a bar:
	//   min=-100 and max=100, use b.zero
	//   min = 10 and max = 20, use b.bottom
	//
	public override function get_y_bottom( right_axis:Bool = false ):Float
	{
		//
		// may have min=10, max=20, or
		// min = 20, max = -20 (upside down chart)
		//
		var r:Range = right_axis ? this.y_right_range : this.y_range;
		
		var min:Int = r.min;
		var max:Int = r.max;
		min = Math.min( min, max );
		
		return this.get_y_from_val( Math.max(0,min), right_axis );
	}
	
	// takes a value and returns the screen Y location
	public function getY_old( i:Float, right_axis:Bool ):Float
	{
		var r:Range = right_axis ? this.y_right_range : this.y_range;
		
		var steps:Int = this.height / (r.count());// ( right_axis ));
		
		// find Y pos for value=zero
		var y:Int = this.bottom-(steps*(r.min*-1));
		
		// move up (-Y) to our Point (don't forget that y_min will shift it down)
		y -= i*steps;
		return y;
	}
	
	//
	// takes a value and returns the screen Y location
	// what is the Y range?
	//
	// Horizontal bar charts are offset. Note:
	//   step = 1
	//   and step/2 is offset at the bottom and top
	// so we add 1*step so we can calculate:
	//
	//   offset = true 
	//
	//     |
	//  X -|==========
	//     |
	//  Y -|===
	//     |
	//  Z -|========
	//     +--+--+--+--+--+--
	//
	// offset = false
	//
	//  2 -|
	//     |
	//  1 -|  0--0--0--0--0
	//     |
	//  0 -+--+--+--+--+--+--
	//
	public override function get_y_from_val( i:Float, right_axis:Bool = false ):Float {
		
		var r:Range = right_axis ? this.y_right_range : this.y_range;
		
		var steps:Int = this.height / r.count();
		
		// trace( 'off' );
		// trace( this.y_offset.offset );
		// trace( count );
		
		var tmp:Int = 0;
		if( this.y_offset )
			tmp = (steps / 2);
			
		// move up (-Y) to our Point (don't forget that y_min will shift it down)
		return this.bottom-tmp-(r.min-i)*steps*-1;
	}
	
	public override function get_get_x_from_pos_and_y_from_val( index:Float, y:Float, ?right_axis:Bool = false ):flash.geom.Point {
		
		return new flash.geom.Point(
			this.get_x_from_pos( index ),
			this.get_y_from_val( y, right_axis ) );
	}
	
	public function width_():Float
	{
		return this.right-this.left_();
	}
	
	function left_():Float
	{
		var padding_left:Int = this.tick_offset;
		return this.left+padding_left;
	}
	
	//
	// Scatter and Horizontal Bar charts use this:
	//
	//   get the x position by value
	//  (e.g. what is the x position for -5 ?)
	//
	public override function get_x_from_val( i:Float ):Float {
		// Patch from DZ:
		var rev:Bool = this.x_range.min > this.x_range.max;
		var count:Int = this.x_range.count();
		count += (rev && this.x_range.offset) ? -2 : 0;
		var item_width:Int = this.width_() / count;
		// end DZ
		
		
		var pos:Int = i-this.x_range.min;
		
		var tmp:Int = 0;
		if( this.x_offset )
			tmp = Math.abs(item_width/2);
			
		return this.left_()+tmp+(pos*item_width);
	}
	
	//
	// get the x location of the n'th item
	//
	public override function get_x_from_pos( i:Float ):Float {
		// DZ:
//			var item_width:Float = Math.abs(this.width_() / this.x_range.count());
		var rev:Bool = this.x_range.min > this.x_range.max;
		var count:Int = this.x_range.count();
		count += (rev && this.x_range.offset) ? -2 : 0;
		var item_width:Int = Math.abs(this.width_() / count);
			
		var tmp:Int = 0;
		if( this.x_offset )
			tmp = (item_width/2);

		return this.left_()+tmp+(i*item_width);
	}
	
	//
	// get the position of the n'th X axis tick
	//
	public function get_x_tick_pos( i:Float ):Float
	{
		return this.get_x_from_pos(i) - this.tick_offset;
	}

	
	//
	// make a Point object, using the absolute values (e.g. -5,-5 )
	/*
	public function make_Point_2( x:Float, y:Float, right_axis:Bool ):charts.Elements.Point
	{
		return new charts.Elements.Point(
			this.get_x_from_val( x ),
			this.get_y_from_val( y, right_axis )
			
			// whats this for?
			//,y
			);
	}*/
	
	public function set_bar_groups( n:Float ): Void {
		this.bar_groups = n;
	}
	
	//
	// index: the n'th bar from the left
	//
	public function get_bar_coords( index:Float, group:Float ):Dynamic {
		var item_width:Int = this.width_() / this.x_range.count();
		
		// the bar(s) have gaps between them:
		var bar_set_width:Float = item_width*0.8;
		
		// get the margin between sets of bars:
		var tmp:Int = 0;
		if( this.x_offset )
			tmp = item_width;
		
		// 1 bar == 100% wide, 2 bars = 50% wide each
		var bar_width:Int = bar_set_width / this.bar_groups;
		//bar_width -= 0.001;		// <-- hack so bars don't quite touch
		
		var bar_left:Int = this.left_()+((tmp-bar_set_width)/2);
		var left:Int = bar_left+(index*item_width);
		left += bar_width * group;
		
		return { x:left, width:bar_width };
	}
	
	public function get_horiz_bar_coords( index:Float, group:Float ):Dynamic {
		
		// split the height Into equal heights for bar
		var bar_width:Int = this.height / (this.y_range.count()+1);
		
		// the bar(s) have gaps between them:
		var bar_set_width:Float = bar_width*0.8;
		
		// 1 bar == 100% wide, 2 bars = 50% wide each
		var group_width:Int = bar_set_width / this.bar_groups;
		
		var bar_top:Int = this.top+((bar_width-bar_set_width)/2);
		var top:Int = bar_top+(index*bar_width);
		top += group_width * group;
		
		return { y:top, width:group_width };
	}
	
	
	public function makePointHLC( x:Float, high:Float, close:Float, low:Float, right_axis:Bool, group:Float, group_count:Float )
	:PointHLC {

		var item_width:Int = this.width_() / this.x_range.count();
		// the bar(s) have gaps between them:
		var bar_set_width:Int = item_width*1;

		// get the margin between sets of bars:
		var bar_left:Int = this.left_()+((item_width-bar_set_width)/2);
		// 1 bar == 100% wide, 2 bars = 50% wide each
		var bar_width:Int = bar_set_width/group_count;

		var left:Int = bar_left+(x*item_width);
		left += bar_width*group;

		return new PointHLC(
			left,
			this.get_y_from_val( high, right_axis ),
			this.get_y_from_val( close, right_axis ),
			this.get_y_from_val( low, right_axis ),
			high,
			bar_width
//				,close
			);

	}
}
